package serialisierung;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import org.apache.openjpa.persistence.OpenJPAPersistence;

import fpt.com.Product;

public class OpenJPAStrategy implements fpt.com.SerializableStrategy {

	private EntityManager e;
	private int i = 0;
	private EntityManagerFactory fac;
	private List<?> resultList;
	private EntityTransaction t;

	public OpenJPAStrategy() {

	}

	// Produkte aus DB auslesen
	@Override
	public Product readObject() throws IOException {

		// Einmalig bei jeder neuen Deserialisierung
		if (fac == null || !fac.isOpen()) {
			fac = getWithoutConfig();
			e = fac.createEntityManager();
			resultList = new ArrayList<>();
		}

		if (resultList.isEmpty()) {
			// Ausgelesene Produkte werden in resultList gespeichert (20 Stück)
			t = e.getTransaction();
			t.begin();
			resultList = e.createQuery("SELECT p FROM Product p ORDER BY p.id DESC")
					.setMaxResults(20).getResultList();
			t.commit();
		}

		// Ausgelesene Produkte ausgeben
		if (i == resultList.size()) {
			i = 0;
			// Nach Durchlaufen der Liste null zurückgeben
			return null;
		} else {
			Product productToReturn = (Product) resultList.get(i);
			i++; // Counter zum Durchlaufen der Liste
			return productToReturn;
		}

	}

	// Produkt in DB speichern
	@Override
	public void writeObject(Product product) throws IOException {
		// Einmalig bei jeder neuen Serialisierung
		if (fac == null || !fac.isOpen()) {
			fac = getWithoutConfig();
			e = fac.createEntityManager();
		}

		// Sichergehen, dass Produkte, welche in DB gespeichert werden sollen,
		// keine ID haben
		//Anmerkung: Es ist eine inhaltliche Überlegung, ob dies gewünscht ist
		//alternativ könnte auch ein Update der bereits vorhandenen Einträge stattfinden
		//oder eine Fehlermeldung ausgegeben werden
		if (product.getId() != 0) {
			product.setId(0);
		}

		t = e.getTransaction();
		t.begin();
		e.persist(product); // Produkt in DB einfügen
		t.commit();
	}

	@Override
	public void close() {
		System.out.println("Closing");
		if (e.isOpen()) {
			e.close();
		}
		if (fac.isOpen()) {
			fac.close();
		}
	}

	public static EntityManagerFactory getWithoutConfig() {

		// Verbindungsparameter angeben
		Map<String, String> map = new HashMap<String, String>();

		map.put("openjpa.ConnectionURL", "jdbc:postgresql://java.is.uni-due.de/ws1011");
		map.put("openjpa.ConnectionDriverName", "org.postgresql.Driver");
		map.put("openjpa.ConnectionUserName", "ws1011");
		map.put("openjpa.ConnectionPassword", "ftpw10");
		map.put("openjpa.RuntimeUnenhancedClasses", "supported");
		map.put("openjpa.jdbc.SynchronizeMappings", "false");

		// Persistente Klasse angeben
		map.put("openjpa.MetaDataFactory", "jpa(Types=" + model.Product.class.getName() + ")");

		// Folgendes wird benötigt, sobald wir mehr als eine persistente Klasse haben
		// Solange Product.class die einzige ist, geht es auch einfacher (siehe oben)
		// Klassen zur Registrierung angeben
//		List<Class<?>> types = new ArrayList<Class<?>>();
//		types.add(Product.class);
//		if (!types.isEmpty()) {
//			StringBuffer buf = new StringBuffer();
//			for (Class<?> c : types) {				
//				if (buf.length() > 0)
//					buf.append(";");
//				buf.append(c.getName());
//			}
//			map.put("openjpa.MetaDataFactory", "jpa(Types=" + buf.toString() + ")");
//		}

		return OpenJPAPersistence.getEntityManagerFactory(map);

	}

}
